package Game.Spawners;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.newdawn.slick.SlickException;

import Game.GameHelper;
import Game.Level;
import Game.MapBlock.EMapBlockType;
import Game.Objects.GODoor;
import Game.Objects.GODoorKey;
import Game.Objects.GameObject;
import GameMath.GPoint;
import GameMath.GSize;
import Tools.Pathfinding.Pathfinding;

/** Spawn Doors class
 * @author Aikidos
 */
public class SpawnDoors extends SpawnerBase {

	@Override
	public void spawn(Level level) throws SlickException,
			InstantiationException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException,
			NoSuchMethodException, SecurityException, ClassNotFoundException {
		
		if (!level.isUndergroundTown()) {
			// Create list of door place
			List<GPoint> places = new ArrayList<GPoint>();

			// Template #1
			{
				EMapBlockType[][] template = {
						{ EMapBlockType.Floor, EMapBlockType.Floor,
								EMapBlockType.Floor },
						{ EMapBlockType.Wall, EMapBlockType.Floor,
								EMapBlockType.Wall } };

				List<GPoint> newPlaces = level.getMap().getPlacesByTemplate(
						template, new GSize(2, 3));
				for (GPoint p : newPlaces) {
					GPoint np = new GPoint(p);
					np.x += 1;
					np.y += 1;
					places.add(np);
				}
			}

			// Template #2
			{
				EMapBlockType[][] template = {
						{ EMapBlockType.Wall, EMapBlockType.Floor,
								EMapBlockType.Wall },
						{ EMapBlockType.Floor, EMapBlockType.Floor,
								EMapBlockType.Floor } };

				List<GPoint> newPlaces = level.getMap().getPlacesByTemplate(
						template, new GSize(2, 3));
				for (GPoint p : newPlaces) {
					GPoint np = new GPoint(p);
					np.y += 1;
					places.add(np);
				}
			}

			// Template #3
			{
				EMapBlockType[][] template = {
						{ EMapBlockType.Floor, EMapBlockType.Wall },
						{ EMapBlockType.Floor, EMapBlockType.Floor },
						{ EMapBlockType.Floor, EMapBlockType.Wall } };

				List<GPoint> newPlaces = level.getMap().getPlacesByTemplate(
						template, new GSize(3, 2));
				for (GPoint p : newPlaces) {
					GPoint np = new GPoint(p);
					np.x += 1;
					np.y += 1;
					places.add(np);
				}
			}

			// Template #4
			{
				EMapBlockType[][] template = {
						{ EMapBlockType.Wall, EMapBlockType.Floor },
						{ EMapBlockType.Floor, EMapBlockType.Floor },
						{ EMapBlockType.Wall, EMapBlockType.Floor } };

				List<GPoint> newPlaces = level.getMap().getPlacesByTemplate(
						template, new GSize(3, 2));
				for (GPoint p : newPlaces) {
					GPoint np = new GPoint(p);
					np.x += 1;
					places.add(np);
				}
			}

			// Remove bad places
			{
				GPoint leverCoord = level.getObjectManager().getObjectsByClass(
						"Game.Objects.GOLever")[0].getCoord();
				GPoint playerCoord = level.getObjectManager().getPlayer()
						.getCoord();
				GPoint trapdoorCoord = level.getObjectManager()
						.getObjectsByClass("Game.Objects.GOTrapdoor")[0]
						.getCoord();
				GameObject[] chests = level.getObjectManager()
						.getObjectsByClass("Game.Objects.GOChest");
				GameObject[] goldChests = level.getObjectManager()
						.getObjectsByClass("Game.Objects.GOGoldChest");
				for (int i = 0; i < places.size(); i++) {
					if ((places.get(i).x == leverCoord.x)
							&& (places.get(i).y == leverCoord.y)) {
						places.remove(i);
						i = 0;
					}

					if ((places.get(i).x == playerCoord.x)
							&& (places.get(i).y == playerCoord.y)) {
						places.remove(i);
						i = 0;
					}

					if ((places.get(i).x == trapdoorCoord.x)
							&& (places.get(i).y == trapdoorCoord.y)) {
						places.remove(i);
						i = 0;
					}

					for (GameObject chest : chests) {
						if ((places.get(i).x == chest.getCoord().x)
								&& (places.get(i).y == chest.getCoord().y)) {
							places.remove(i);
							i = 0;
							break;
						}
					}

					for (GameObject goldChest : goldChests) {
						if ((places.get(i).x == goldChest.getCoord().x)
								&& (places.get(i).y == goldChest.getCoord().y)) {
							places.remove(i);
							i = 0;
							break;
						}
					}
				}
			}

			int numOfDoors = (places.size() >= 2) ? 2 : places.size();

			// ////////////////////////////
			// // Spawn doors and keys ////
			// ////////////////////////////
			Random rnd = GameHelper.getRandom();

			// Get player position
			GPoint playerCoord = level.getObjectManager().getPlayer()
					.getCoord();

			// Get map size
			GSize mapSize = level.getMap().getSize();

			for (int i = 0; i < numOfDoors; i++) {
				GPoint p = places.get(rnd.nextInt(places.size()));

				GODoor newDoor = new GODoor(level.getObjectManager());
				newDoor.setId(i + 1);
				newDoor.setCoord(p);

				places.remove(p);

				// Set pathfinding and lighting solid maps
				Pathfinding.getPathfindingMap().getSolidMap()[p.x][p.y] = 1;
				level.getLightManager().getSolidMap()[p.x][p.y] = true;

				// Spawn key
				GODoorKey newKey = new GODoorKey(level.getObjectManager());
				newKey.setId(i + 1);

				// Set key position
				GPoint keyPos = new GPoint(rnd.nextInt(mapSize.width),
						rnd.nextInt(mapSize.height));
				while (true) {
					boolean solve = true;

					// Can be found?
					if (level.getMap().getDistance(playerCoord, keyPos) == -1)
						solve = false;

					// Spawn under chest?
					GameObject[] chests = level.getObjectManager()
							.getObjectsByClass("Game.Objects.GOGoldChest");
					for (GameObject chest : chests) {
						if ((keyPos.x == chest.getCoord().x)
								&& (keyPos.y == chest.getCoord().y)) {
							solve = false;
						}
					}

					// Spawn under lever?
					GameObject lever = level.getObjectManager()
							.getObjectsByClass("Game.Objects.GOLever")[0];
					if ((keyPos.x == lever.getCoord().x)
							&& (keyPos.y == lever.getCoord().y)) {
						solve = false;
					}

					// Spawn under trapdoor?
					GameObject trapdoor = level.getObjectManager()
							.getObjectsByClass("Game.Objects.GOTrapdoor")[0];
					if ((keyPos.x == trapdoor.getCoord().x)
							&& (keyPos.y == trapdoor.getCoord().y)) {
						solve = false;
					}

					// Spawn under altar?
					GameObject altar = level.getObjectManager()
							.getObjectsByClass("Game.Objects.GOAltar")[0];
					if ((keyPos.x == altar.getCoord().x)
							&& (keyPos.y == altar.getCoord().y)) {
						solve = false;
					}

					if (solve)
						break;

					keyPos.x = rnd.nextInt(mapSize.width);
					keyPos.y = rnd.nextInt(mapSize.height);
				}

				newKey.setCoord(keyPos);
			}
		}
	}
	
}
